import { FileInfo, FileUploadConfig, FileUploadListener } from "../FileServerClient";
import UUID from "../../../common/UUID";
import Beans from "../../../common/utils";
import Logger from "../../../log/Logger";
import AbstractXMLHttpClient from "./AbstractXMLHttpClient";

class HtmlFormStrut {
    public target:HTMLElement|any;
    public form:HTMLFormElement|any;
    public file:HTMLElement|any;
    public inputs:any = {}; // key:HTMLElement
}

/**
 *  浏览器端 xmlhttp文件上传客户端.
 *  适用SDK 平台: WEB
 *  配置方式
     sdkConfig.fileServerConfig = {
            use: 'aliyun',
            // use: 'local',
            client: 'xmlhttp',
            baseUrl: 'http://192.168.0.100:8082',
            version: 'v1',
        };
 *
 * */
export default class BrowserXMLHttpClient extends AbstractXMLHttpClient{
    private formStrut:HtmlFormStrut|any;
    upload(path:string):string{
        throw new Error('Not support the method, please use ReactNativeXMLHttpClient/ReactNativeFetchClient.');
    }
    init(uploadConfig: FileUploadConfig, listeners: FileUploadListener | undefined): void {
        const self = this;
        self.uploadConfig = uploadConfig;
        if(listeners !== undefined){
            this.beforeUploadCallback = listeners.beforeUploadCallback;
            this.progressCallback = listeners.progressCallback;
            this.uploadedCallback = listeners.uploadedCallback;
            this.errorCallback = listeners.errorCallback;
        }

        const onFileSelectedHandler:(files:Array<any>, formStrut:HtmlFormStrut)=>void = function(ffs:Array<any>, formStrut:HtmlFormStrut){
            if(!self.uploadConfig){
                return;
            }
            const fileQueue:Array<any> = [];
            // 去除文件外的元素
            for (const f in ffs) {
                const ff = ffs[f];
                if (ff && ff.name && typeof ff === 'object') {
                    if(self.uploadConfig.allowSuffix && self.uploadConfig.allowSuffix.length > 0){
                        const suffix = ff.name.substring(ff.name.lastIndexOf(".") + 1);
                        if(self.uploadConfig.allowSuffix.indexOf(suffix) < 0){
                            throw new Error(`选择的文件:${ff.name} 不在设置的后缀名列表:${self.uploadConfig.allowSuffix.join()}内`);
                        }
                    }
                    // 是否超出大小
                    if (self.uploadConfig.maxFileSize && ff.size > self.getSettingMaxSize()) {
                        const fileSizeMb = parseFloat(ff.size/(1024*1024)+'').toFixed(2);
                        throw new Error(`选择的文件:${ff.name}(${fileSizeMb}mb)超出设置的文件大小上限:${self.uploadConfig.maxFileSize}mb`);
                    }
                    fileQueue.push({
                        id: UUID.gen(),
                        name: ff.name.replace(/[\\\!\$\^\*\(\)\-\=\+\[\]\{\}\|\:\/\<\>\?,`'~@#%&;\" ]/g, ""),
                        originFileName: ff.name,
                        size: parseFloat(ff.size/1024+'').toFixed(0),
                        url: '',
                        file: ff
                    })
                }
            }

            if(fileQueue.length > 1){
                self.clearFileSelected();
                self.setErrorCallback(700, "一次仅能上传一个文件");
                return;
            }
            // 触发用户额外自定义的 beforeUploadCallback 事件
            let fileInfo = new FileInfo();
            fileInfo.id = fileQueue[0].id;
            fileInfo.name = fileQueue[0].name;
            fileInfo.originFileName = fileQueue[0].originFileName;
            fileInfo.size = parseFloat(fileQueue[0].size);
            fileInfo.type = self.getFileType(fileQueue[0].originFileName);
            if(typeof self.beforeUploadCallback === 'function'){
                const info = self.beforeUploadCallback(fileInfo);
                if(info){
                    self.fileInfo = info;
                }
                else {
                    self.fileInfo = fileInfo;
                }
            }
            else {
                self.fileInfo = fileInfo;
            }
            // 开始上传第一个文件
            if (fileQueue.length > 0 && fileQueue[0]) {
                // 取得上传签名, 上传文件
                self.buildFormParams(fileInfo, function (host, formData) {
                    self.applyBrowserXMLHttpUploadDo(host, formData, formStrut)
                });
            }
        };

        self.createSubmitFormDom(self.uploadConfig.domId, onFileSelectedHandler);
    }


    // 本地上传的细节处理 self: unuploader 实例, file: 当前文件
    private applyBrowserXMLHttpUploadDo(host, formData:any, formStrut: HtmlFormStrut):void {
        const self = this;
        if(!self.signInfo || !self.fileInfo){
            return;
        }
        let uploadUrl = '';
        if(this.serverConfig.use === 'local'){
            uploadUrl = (self.serverConfig.baseUrl + '/' + self.serverConfig.version + '/' + 'common/file/upload');
        }
        else if(this.serverConfig.use === 'aliyun'){
            uploadUrl = host;
        }
        const url = uploadUrl;
        // formStrut.signature.setAttribute('value', self.signInfo.signature);
        for(const v in formData){
            self.setInputFormValue(v, formData[v]);// 设置form 参数
        }
        formStrut.file.setAttribute('name', 'file');
        formStrut.file.setAttribute('fileName', self.fileInfo.originFileName);
        const fd = new FormData(formStrut.form);
        // 创建XMLHttpRequest对象
        const xhr = new XMLHttpRequest();
        // 调用open方法准备ajax请求
        xhr.open('post', uploadUrl);
        // 绑定onprogress事件
        xhr.upload.onprogress = function (evt) {
            const percent = (evt.loaded / evt.total).toFixed(2);
            // 显示进度条
            if(typeof self.progressCallback === 'function' && self.fileInfo){
                self.fileInfo.uploadPercent = parseFloat(percent) * 100;
                self.progressCallback(self.fileInfo);
            }
        };
        xhr.onload = function () {
            if(self.serverConfig.use === 'local'){
                self.handleLocalServerResponse(xhr);
            }
            else if(self.serverConfig.use === 'aliyun'){
                self.handleAliyunServerResponse(xhr);
            }
        };
        Logger.info(`ready to post:${url} with params:${Beans.json(formData)}`);
        // 发送请求
        xhr.send(fd);
    };



    private getSettingMaxSize():number {
        const maxSize = (this.uploadConfig && this.uploadConfig.maxFileSize) ? this.uploadConfig.maxFileSize : 10*1024;// 10gb
        return maxSize * 1024 * 1024;
    }


    // ---------- html 相关 ----------
    /**
     *  创建HTML 提交表单
     *
     *  返回的DOM结构是这样的:
     *  <TARGET id='domId'>
     *      <form id='form-xxx'>
     *          <input id='signature-xxx' type='text'/>
     *          <input id='file-xxx' type='file'/>
     *      </form>
     *  <TARGET>
     *
     * */
    // 表单提交的参数
    LOCAL_SERVER_INPUT_FORM = ['signature'];
    ALIYUN_SERVER_INPUT_FORM = ['key','policy','OSSAccessKeyId','success_action_status','callback','signature'];
    private createSubmitFormDom(domId:string, onFileSelect:(files:Array<any>,formStrut:HtmlFormStrut)=>void):any {
        const self = this;
        const formStrut = new HtmlFormStrut();
        self.formStrut = formStrut;
        const domFormId = 'form-' + UUID.gen();
        const domFileId = 'file-' + UUID.gen();

        const domClick = document.getElementById(domId);
        if(!domClick){
            self.setErrorCallback(703, `domId:${domId} 不存在.`);
            return;
        }
        // domClick 不能是input type='file' 的元素
        if(domClick.tagName.toLocaleLowerCase() === 'input' && domClick.getAttribute('type') === 'file'){
            self.setErrorCallback(704, `domId:${domId} 关联的DOM不能是<input type ='file'>类型.`);
            return;
        }
        const domForm = document.createElement('form');
        if(this.serverConfig.use === 'local'){
            this.LOCAL_SERVER_INPUT_FORM.forEach((v)=>this.createInputForm(domForm, v))
        }
        else if(this.serverConfig.use === 'aliyun'){
            this.ALIYUN_SERVER_INPUT_FORM.forEach((v)=>this.createInputForm(domForm, v))
        }
        // file muse be the last dom
        const domFile = document.createElement('input');
        domForm.setAttribute('id', domFormId);
        domForm.setAttribute('method', 'POST');
        domForm.setAttribute('enctype', 'multipart/form-data');
        domForm.setAttribute('action', '');
        domFile.setAttribute('id', domFileId);
        domFile.setAttribute('type', 'file');
        domForm.appendChild(domFile);
        domClick.appendChild(domForm);
        // 设置事件
        domClick.onclick = function () {
            if(self.formStrut){
                self.formStrut.file.click();
            }
        };
        // 隐藏
        domForm.style.display = 'none';
        formStrut.target = domClick;
        formStrut.form = domForm;
        // formStrut.signature = domInputSignature;
        formStrut.file = domFile;
        self.formStrut.file.onchange = function () {
            try{
                // @ts-ignore
                onFileSelect(this.files, formStrut);
            }
            catch (e) {
                Logger.trace(e);
                self.clearFileSelected();
                self.setErrorCallback(701, e+'');
            }
        }
    }

    private createInputForm(parentDom:HTMLElement, key){
        const inputId = 'input-' + UUID.gen();
        const domInput = document.createElement('input');
        domInput.setAttribute('id', inputId);
        domInput.setAttribute('type', 'text');
        domInput.setAttribute('name', key);

        parentDom.appendChild(domInput);
        this.formStrut.inputs[key] = {
            id:inputId,
            dom:domInput
        }
    }

    private setInputFormValue(key, value){
        if(this.formStrut.inputs.hasOwnProperty(key)){
            const dom = document.getElementById(this.formStrut.inputs[key].id);
            dom!.setAttribute('value', value);
        }
    }

    // 允许重复选择-清除已选择的文件
    private clearFileSelected():void{
        if(this.formStrut && this.formStrut.file){
            // @ts-ignore
            this.formStrut.file.value = ''; // 允许重复选择
        }
    }
}
